//================================
//  MagicaVoxel [12/09/2013]
//  Copyright(c) 2013 @ ephtracy. All rights reserved.
//================================

//================================
// Notice that this code is neither robust nor complete.
// It is only a sample code demonstrating
//     how to load current version .vox model from MagicaVoxel.
//================================

#ifndef __MV_VOX__
#define __MV_VOX__

#include <stdio.h>
#include <string.h>
#include <vector>

#include "mv_lib.h"
#include "mv_file.h"

// default mac palette table
const unsigned int mv_default_palette[ 256 ] = {
	0x00000000, 0xffffffff, 0xffccffff, 0xff99ffff, 0xff66ffff, 0xff33ffff, 0xff00ffff, 0xffffccff, 0xffccccff, 0xff99ccff, 0xff66ccff, 0xff33ccff, 0xff00ccff, 0xffff99ff, 0xffcc99ff, 0xff9999ff,
	0xff6699ff, 0xff3399ff, 0xff0099ff, 0xffff66ff, 0xffcc66ff, 0xff9966ff, 0xff6666ff, 0xff3366ff, 0xff0066ff, 0xffff33ff, 0xffcc33ff, 0xff9933ff, 0xff6633ff, 0xff3333ff, 0xff0033ff, 0xffff00ff,
	0xffcc00ff, 0xff9900ff, 0xff6600ff, 0xff3300ff, 0xff0000ff, 0xffffffcc, 0xffccffcc, 0xff99ffcc, 0xff66ffcc, 0xff33ffcc, 0xff00ffcc, 0xffffcccc, 0xffcccccc, 0xff99cccc, 0xff66cccc, 0xff33cccc,
	0xff00cccc, 0xffff99cc, 0xffcc99cc, 0xff9999cc, 0xff6699cc, 0xff3399cc, 0xff0099cc, 0xffff66cc, 0xffcc66cc, 0xff9966cc, 0xff6666cc, 0xff3366cc, 0xff0066cc, 0xffff33cc, 0xffcc33cc, 0xff9933cc,
	0xff6633cc, 0xff3333cc, 0xff0033cc, 0xffff00cc, 0xffcc00cc, 0xff9900cc, 0xff6600cc, 0xff3300cc, 0xff0000cc, 0xffffff99, 0xffccff99, 0xff99ff99, 0xff66ff99, 0xff33ff99, 0xff00ff99, 0xffffcc99,
	0xffcccc99, 0xff99cc99, 0xff66cc99, 0xff33cc99, 0xff00cc99, 0xffff9999, 0xffcc9999, 0xff999999, 0xff669999, 0xff339999, 0xff009999, 0xffff6699, 0xffcc6699, 0xff996699, 0xff666699, 0xff336699,
	0xff006699, 0xffff3399, 0xffcc3399, 0xff993399, 0xff663399, 0xff333399, 0xff003399, 0xffff0099, 0xffcc0099, 0xff990099, 0xff660099, 0xff330099, 0xff000099, 0xffffff66, 0xffccff66, 0xff99ff66,
	0xff66ff66, 0xff33ff66, 0xff00ff66, 0xffffcc66, 0xffcccc66, 0xff99cc66, 0xff66cc66, 0xff33cc66, 0xff00cc66, 0xffff9966, 0xffcc9966, 0xff999966, 0xff669966, 0xff339966, 0xff009966, 0xffff6666,
	0xffcc6666, 0xff996666, 0xff666666, 0xff336666, 0xff006666, 0xffff3366, 0xffcc3366, 0xff993366, 0xff663366, 0xff333366, 0xff003366, 0xffff0066, 0xffcc0066, 0xff990066, 0xff660066, 0xff330066,
	0xff000066, 0xffffff33, 0xffccff33, 0xff99ff33, 0xff66ff33, 0xff33ff33, 0xff00ff33, 0xffffcc33, 0xffcccc33, 0xff99cc33, 0xff66cc33, 0xff33cc33, 0xff00cc33, 0xffff9933, 0xffcc9933, 0xff999933,
	0xff669933, 0xff339933, 0xff009933, 0xffff6633, 0xffcc6633, 0xff996633, 0xff666633, 0xff336633, 0xff006633, 0xffff3333, 0xffcc3333, 0xff993333, 0xff663333, 0xff333333, 0xff003333, 0xffff0033,
	0xffcc0033, 0xff990033, 0xff660033, 0xff330033, 0xff000033, 0xffffff00, 0xffccff00, 0xff99ff00, 0xff66ff00, 0xff33ff00, 0xff00ff00, 0xffffcc00, 0xffcccc00, 0xff99cc00, 0xff66cc00, 0xff33cc00,
	0xff00cc00, 0xffff9900, 0xffcc9900, 0xff999900, 0xff669900, 0xff339900, 0xff009900, 0xffff6600, 0xffcc6600, 0xff996600, 0xff666600, 0xff336600, 0xff006600, 0xffff3300, 0xffcc3300, 0xff993300,
	0xff663300, 0xff333300, 0xff003300, 0xffff0000, 0xffcc0000, 0xff990000, 0xff660000, 0xff330000, 0xff0000ee, 0xff0000dd, 0xff0000bb, 0xff0000aa, 0xff000088, 0xff000077, 0xff000055, 0xff000044,
	0xff000022, 0xff000011, 0xff00ee00, 0xff00dd00, 0xff00bb00, 0xff00aa00, 0xff008800, 0xff007700, 0xff005500, 0xff004400, 0xff002200, 0xff001100, 0xffee0000, 0xffdd0000, 0xffbb0000, 0xffaa0000,
	0xff880000, 0xff770000, 0xff550000, 0xff440000, 0xff220000, 0xff110000, 0xffeeeeee, 0xffdddddd, 0xffbbbbbb, 0xffaaaaaa, 0xff888888, 0xff777777, 0xff555555, 0xff444444, 0xff222222, 0xff111111
};

//================
// Model
//================
class MV_Model {
public :
	// version
	int version;

	// size
	MV_Vec3i size;

	// voxels
	std::vector< MV_Voxel > voxels;

	// palette
	MV_RGBA palette[ 256 ];

public :
	MV_Model() :
		version( 0 )
	{
	}

	void Free( void ) {
		version = 0;
		size.Zero();
		voxels.clear();
	}

	bool LoadModel( const char *path ) {
		// free old data
		Free();
		if ( !ReadModelFile( path ) ) {
			// free invalid data
			Free();
			return false;
		}
		return true;
	}

public :
	struct chunk_t {
		int id;
		int contentSize;
		int childrenSize;
		int end;
	};

	bool ReadModelFile( const char *path ) {
		MV_ReadFile file;
		if ( !file.Open( path ) ) {
			printf( "error : cannot open file \"%s\"\n", path );
			return false;
		}

		const int MV_VERSION = 150;

		const int ID_VOX  = MV_ID( 'V', 'O', 'X', ' ' );
		const int ID_MAIN = MV_ID( 'M', 'A', 'I', 'N' );
		const int ID_SIZE = MV_ID( 'S', 'I', 'Z', 'E' );
		const int ID_XYZI = MV_ID( 'X', 'Y', 'Z', 'I' );
		const int ID_RGBA = MV_ID( 'R', 'G', 'B', 'A' );

		// magic number
		int magic = file.ReadInt();
		if ( magic != ID_VOX ) {
			printf( "error : magic number does not match : %d != %d\n", magic, ID_VOX );
			return false;
		}

		// version
		version = file.ReadInt();
		if ( version != MV_VERSION ) {
			printf( "error : version does not match : %d != %d\n", version, MV_VERSION );
			return false;
		}

		// copy default palette
		memcpy( palette, mv_default_palette, sizeof( mv_default_palette ) );

		// main chunk
		chunk_t mainChunk;
		ReadChunk( file, mainChunk );
		if ( mainChunk.id != ID_MAIN ) {
			printf( "error : MAIN chunk is not found\n" );
			return false;
		}

		// skip content of main chunk
		file.Seek( mainChunk.contentSize, SEEK_CUR );

		// read children chunks
		while ( file.Position() < mainChunk.end ) {
			// read chunk header
			chunk_t sub;
			ReadChunk( file, sub );

			if ( sub.id == ID_SIZE ) {
				// size
				file.ReadData( &size, sizeof( MV_Vec3i ), 1 );
				if ( size.x <= 0 || size.y <= 0 || size.z <= 0 ) {
					printf( "error : invalid size : %d %d %d\n", size.x, size.y, size.z );
					return false;
				}
			}
			else if ( sub.id == ID_XYZI ) {
				// numVoxels
				int numVoxels = file.ReadInt();
				if ( numVoxels < 0 ) {
					printf( "error : negative number of voxels : %d\n", numVoxels );
					return false;
				}

				// voxels
				if ( numVoxels > 0 ) {
					voxels.resize( numVoxels );
					file.ReadData( voxels.data(), sizeof( MV_Voxel ), numVoxels );
				}
			}
			else if ( sub.id == ID_RGBA ) {
				// NOTICE : the color 0~254 are mapped to the palette 1~255
				file.ReadData( palette + 1, sizeof( MV_RGBA ), 255 );

				// NOTICE : skip the last unused color
				MV_RGBA unused;
				file.ReadData( &unused, sizeof( MV_RGBA ), 1 );
			}

			// skip unread bytes of current chunk or the whole unused chunk
			file.Seek( sub.end, SEEK_SET );
		}

		// print model info
		printf( "log : model : size %d %d %d : numVoxels %d\n", size.x, size.y, size.z, voxels.size() );

		return true;
	}

	void ReadChunk( MV_ReadFile &file, chunk_t &chunk ) {
		// read chunk header
		chunk.id = file.ReadInt();
		chunk.contentSize  = file.ReadInt();
		chunk.childrenSize = file.ReadInt();

		// end of chunk : used for skipping the whole chunk
		chunk.end = file.Position() + chunk.contentSize + chunk.childrenSize;

		// print chunk info
		{
			const char *c = ( const char * )( &chunk.id );
			printf( "log : chunk : %c%c%c%c : %d %d\n",
				c[0], c[1], c[2], c[3],
				chunk.contentSize, chunk.childrenSize
			);
		}
	}
};

#endif // __MV_VOX__
